package org.geektimes.projects.microprofile.config.source;

import org.eclipse.microprofile.config.spi.ConfigSource;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 抽象的 ConfigSource
 *
 * @author biwin
 */
public abstract class MapBasedConfigSource implements ConfigSource {

    private final int ordinal;
    private final String name;
    private boolean initializedSource = false;
    private static final Object lock = new Object();
    private Map<String, String> source;

    public MapBasedConfigSource(int ordinal, String name) {
        this(ordinal, name, false);
    }

    public MapBasedConfigSource(int ordinal, String name, boolean lazy) {
        this.ordinal = ordinal;
        this.name = name;
        if (!lazy) {
            this.source = getProperties();
        }
    }

    @Override
    public Map<String, String> getProperties() {
        Map<String, String> configData = new HashMap<>();
        try {
            prepareConfigData(configData);
        } catch (Throwable cause) {
            throw new IllegalStateException("准备配置数据发生错误", cause);
        }
        return configData;
    }

    /**
     * 提供给子类实现,以方便 ConfigSource 初始化时加载默认的数据
     *
     * @param configData
     * @throws Throwable
     */
    protected abstract void prepareConfigData(Map configData) throws Throwable;

    @Override
    public Set<String> getPropertyNames() {
        lazyLoadSource();
        return source.keySet();
    }

    @Override
    public final int getOrdinal() {
        return ordinal;
    }

    @Override
    public String getValue(String propertyName) {
        lazyLoadSource();
        return source.get(propertyName);
    }

    @Override
    public final String getName() {
        return name;
    }

    private void lazyLoadSource() {
        if (initializedSource) {
            return;
        }
        if (Objects.isNull(source) || source.isEmpty()) {
            synchronized (lock) {
                if (Objects.isNull(source) || source.isEmpty()) {
                    source = getProperties();
                }
                initializedSource = true;
            }
        }
    }
}
